﻿using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public static class Intersection
{
	/// <summary>
	/// Находит точку пересечения луча и плоскости
	/// </summary>
	/// <param name="ray">Луч</param>
	/// <param name="plane">Плоскость</param>
	/// <param name="max_t">Максимальная координата точки на луче</param>
	/// <param name="ray_t">Возвращаем координата точки пересечения</param>
	/// <returns>Возвращает true, если луч пересекает плоскость</returns>
	public static bool RayPlaneIntersection( Ray ray, Plane plane, float max_t, out float ray_t )
	{
		ray_t = -1f;

		float d = Vector3.Dot( plane.normal, ray.direction );
		if ( Mathf.Abs( d ) < Mathf.Epsilon )
			return false; // плоскость и луч параллельны

		float o = Vector3.Dot( plane.normal, ray.origin );
		ray_t = ( plane.distance - o ) / d;
		if ( ray_t < 0f || ray_t > max_t )
			return false; // точка находится за пределами допустимых значений

		return true;
	}

	public static bool RayRectIntersection( Ray2D ray, Rect rect, float min_t, float max_t, ref float near_t, ref float far_t )
	{
		float t0 = min_t, t1 = max_t;
		bool inside = true;

		for ( int axis = 0; axis < 2; axis++ )
		{
			float ox = ray.origin[ axis ];
			float dx = ray.direction[ axis ];

			float min = ( axis == 0 ) ? rect.xMin : rect.yMin;
			float max = ( axis == 0 ) ? rect.xMax : rect.yMax;

			if ( Mathf.Abs( dx ) <= MathUtils.EPSILON )
			{
				if ( ox < min || ox > max )
					return false;
			}

			float inv_dx = 1.0f / dx;
			float x0 = ( min - ox ) * inv_dx;
			float x1 = ( max - ox ) * inv_dx;

			if ( x1 < x0 )
				MathUtils.Swap( ref x0, ref x1 );

			if ( min_t < x0 )
				inside = false;

			t0 = Mathf.Max( x0, t0 );
			t1 = Mathf.Min( x1, t1 );

			if ( t1 < t0 )
				return false;
		}

		if ( t1 < min_t || t0 > max_t )
			return false;

		near_t = inside ? t1 : t0;
		far_t = t1;

		return true;
	}

	/// <summary>
	/// Пересечение двух прямых a и b, заданных точками 1 и 2
	/// </summary>
	/// <param name="a1"></param>
	/// <param name="a2"></param>
	/// <param name="b1"></param>
	/// <param name="b2"></param>
	public static bool Intersect_Lines( Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2, ref Vector2 result )
	{
		return Intersect_LinesByDir( a1, a2 - a1, b1, b2 - b1, ref result );
	}


	/// <summary>
	/// Пересечение двух прямых a и b, заданных точкой Point и направлением Dir
	/// </summary>
	/// <param name="a1"></param>
	/// <param name="a2"></param>
	/// <param name="b1"></param>
	/// <param name="b2"></param>
	public static bool Intersect_LinesByDir( Vector2 aPoint, Vector2 aDir, Vector2 bPoint, Vector2 bDir, ref Vector2 result )
	{
		Vector2 D = aPoint - bPoint;

		float numer = MathUtils.CrossProduct( bDir, D );
		float denom = MathUtils.CrossProduct( aDir, bDir );

		if ( Mathf.Abs( denom ) < MathUtils.EPSILON )
			return false;

		float u = numer / denom;
		result = aPoint + aDir * u;

		return true;
	}

	public static bool Intersect_LineSegment( Vector2 l1, Vector2 l2, Vector2 s1, Vector2 s2, ref Vector2 result )
	{
		Vector2 L = l2 - l1;
		Vector2 S = s2 - s1;
		Vector2 D = s1 - l1;

		float numer = MathUtils.CrossProduct( L, D );
		float denom = MathUtils.CrossProduct( S, L );

		if ( Mathf.Abs( denom ) < MathUtils.EPSILON )
			return false;

		float u = numer / denom;
		if ( u < -MathUtils.EPSILON || u > 1f + MathUtils.EPSILON )
			return false;

		result = s1 + S * u;
		return true;
	}
}